using System;
using System.Collections.Generic;
using System.Text;

namespace GuavaData
{
    public class Update : Operator
    {
        // DML statement for updating rows in a table

        #region Fields
        string table;
        public string Table
        {
            get { return table; }
            set { table = value; }
        }

        List<string> condColumns;
        public List<string> CondColumns
        {
            get { return condColumns; }
            set { condColumns = value; }
        }

        List<Condition> conditions;
        public List<Condition> Conditions
        {
            get { return conditions; }
            set { conditions = value; }
        }

        List<string> columns;
        public List<string> Columns
        {
            get { return columns; }
            set { columns = value; }
        }

        Operator child;
        public Operator Child
        {
            get { return child; }
            set { child = value; }
        }

        public Dictionary<string, Condition> idJoin;
        #endregion Fields

        public Update(string table, IEnumerable<string> columns, Operator query)
        {
            this.table = table;
            this.condColumns = new List<string>();
            this.conditions = new List<Condition>();
            this.columns = new List<string>(columns);
            this.child = query;
            this.parent = null;
            this.idJoin = null;

            // Check all of the conditions are equality conditions
            foreach (Condition c in this.conditions)
            {
                if (c.CondType != ConditionType.EQ)
                    throw new Exception("Found a condition of type " + c.CondType.ToString() + " in an update statement - only equality conditions are accepted.");
            }

            // Dummy check
            if (query.exposedColumns == null)
                throw new Exception("Query to produce rows does not have any exposed columns, and is of type " + query.GetType().Name + ".");
            if (this.columns.Count != query.exposedColumns.Count)
                throw new Exception("Column counts between expected columns " + this.columns.Count.ToString() + " and query exposed columns " + query.exposedColumns.Count + " do not match.");
        }

        public Update(string table, IEnumerable<string> condColumns, IEnumerable<Condition> conditions, IEnumerable<string> columns, Operator query)
        {
            this.table = table;
            this.condColumns = new List<string>(condColumns);
            this.conditions = new List<Condition>(conditions);
            this.columns = new List<string>(columns);
            this.child = query;
            this.parent = null;
            this.idJoin = null;

            // Check all of the conditions are equality conditions
            foreach (Condition c in this.conditions)
            {
                if (c.CondType != ConditionType.EQ)
                    throw new Exception("Found a condition of type " + c.CondType.ToString() + " in a delete statement - only equality conditions are accepted.");
            }

            // Dummy check
            if (query.exposedColumns == null)
                throw new Exception("Query to produce rows does not have any exposed columns, and is of type " + query.GetType().Name + ".");
            if (this.columns.Count != query.exposedColumns.Count)
                throw new Exception("Column counts between expected columns " + this.columns.Count.ToString() + " and query exposed columns " + query.exposedColumns.Count + " do not match.");
            if (this.condColumns.Count != this.conditions.Count)
                throw new Exception("Column counts between expected condition columns " + this.condColumns.Count.ToString() + " and conditions " + this.conditions.Count + " do not match.");
        }

        public void AddCondition(string column, Condition condition)
        {
            if (condition.CondType != ConditionType.EQ)
                throw new Exception("Found a condition of type " + condition.CondType.ToString() + " in an update statement - only equality conditions are accepted.");
            this.condColumns.Add(column);
            this.conditions.Add(condition);
        }

        public bool needJoin
        {
            get
            {
                return (idJoin != null);
            }
        }

        public string JoinString
        {
            get
            {
                if (idJoin == null || idJoin.Count == 0)
                    return null;

                string build = "";
                foreach (KeyValuePair<string, Condition> p in this.idJoin)
                    build = String.Concat(build, this.table, ".", p.Key, p.Value.ToString(), " AND ");
                return build.Substring(0, build.Length - 5);
            }
        }

        public void AddJoin(string col, Condition cond)
        {
            if (idJoin == null)
                idJoin = new Dictionary<string, Condition>();
            idJoin[col] = cond;
        }

        public string ConditionString
        {
            get
            {
                if (this.conditions == null || this.conditions.Count == 0)
                    return "";

                string build = "";
                for (int i = 0; i < this.conditions.Count; i++)
                    build = String.Concat(build,(needJoin ? this.table + "." : ""), condColumns[i], conditions[i].ToString(), " AND ");
                return build.Substring(0, build.Length - 5);
            }
        }

        public override bool isQuery
        {
            get
            {
                return false;
            }
        }
        public override void NotifyChange(Operator old, Operator newOp)
        {
            if (this.child == old)
            {
                this.child = newOp;
                newOp.parent = this;
            }
        }
        public override SqlText Visit(Provider qv)
        {
            return qv.Transform(this);
        }
        public override void Visit(Transform t, Command c)
        {
            t.Expand(this, c);
        }

        public override Operator Clone()
        {
            Update unew = new Update(this.table, this.condColumns, this.conditions, this.columns, this.child.Clone());
            foreach (string s in this.idJoin.Keys)
                unew.AddJoin(s, this.idJoin[s]);
            return unew;
        }

        public override void Visit(Channel c, string table)
        {
            c.MapAliases(this, table);
        }

    }
}
